﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
using PowersourceUIToolkit;
using UCL.Powersource.Helpers;
using UCL.Powersource.TFS;
using UCL.Powersource.ViewModels.Analysis;

namespace UCL.Powersource.Analysis
{


    [DataContract]
    public class ReportingContext : PropertyChangedInitializedLifeCycle, IReportingContext, ILabel
    {

        public ReportingContext(ITfsTeamProjectCollectionConfiguration tpc, IReportingContextStrategy selector)
        {
            Selector = selector;
            _TfsTeamProjectCollectionConfiguration = tpc;
        }

        private class ReportingContextCachedItem
        {
            public ILabel CachedItem { get; set; }
            public bool IsSelected { get; set; }
        }

        #region TfsTeamProjectCollectionConfiguration

        /// <summary>
        /// Field storage for property TfsTeamProjectCollectionConfiguration
        /// </summary>
        [DataMember]
        private ITfsTeamProjectCollectionConfiguration _TfsTeamProjectCollectionConfiguration = default(ITfsTeamProjectCollectionConfiguration);

        public ITfsTeamProjectCollectionConfiguration TfsTeamProjectCollectionConfiguration
        {
            get { return _TfsTeamProjectCollectionConfiguration; }
        }

        #endregion


        [DataMember]
        public SelectableConfiguration<ITfsTeamProjectConfiguration> SelectableTfsTeamProjectConfigurations { get; private set; }

        [DataMember]
        public SelectableConfiguration<ITimeFrame> SelectableTimeFrames { get; private set; }

        [DataMember]
        public SelectableConfiguration<IStatistic> SelectableStatistics { get; private set; }

        [DataMember]
        private Guid Selector_Guid;
        private IReportingContextStrategy _Selector;

        [IgnoreDataMember]
        public IReportingContextStrategy Selector
        {
            get
            {
                if (_Selector == null)
                {
                    if (Selector_Guid != Guid.Empty)
                        _Selector = IReportingManager.ReportingContextStrategies.FirstOrDefault(a => a.Guid == Selector_Guid);
                    else
                        _Selector = new AllSelected_ReportingContextStrategy();
                }

                return _Selector;
            }
            private set
            {
                _Selector = value;
                if (_Selector == null)
                    Selector_Guid = Guid.Empty;
                else
                    Selector_Guid = _Selector.Guid;
            }
        }

        #region AvailableTfsTeamProjectConfigurations

        /// <summary>
        /// Field storage for property AvailableTfsTeamProjectConfigurations
        /// </summary>
        private IEnumerable<ITfsTeamProjectConfiguration> _AvailableTfsTeamProjectConfigurations = default(IEnumerable<ITfsTeamProjectConfiguration>);

        /// <summary>
        /// Property AvailableTfsTeamProjectConfigurations. TODODOC: Insert appropriate summary here
        /// </summary>
        public IEnumerable<ITfsTeamProjectConfiguration> AvailableTfsTeamProjectConfigurations
        {
            get { return _AvailableTfsTeamProjectConfigurations; }
            set { if (_AvailableTfsTeamProjectConfigurations == value) return; _AvailableTfsTeamProjectConfigurations = value; AvailableTfsTeamProjectConfigurations_RecalculateDependencies(); }
        }

        private void AvailableTfsTeamProjectConfigurations_RecalculateDependencies()
        {
            //Recalculate all properties here

            //NotifyOfPropertyChange is usually placed as last statement - first change all dependencies and then fire property change
            NotifyOfPropertyChange(() => AvailableTfsTeamProjectConfigurations);
        }

        private void AvailableTfsTeamProjectConfigurations_Recalculate()
        {
            _AvailableTfsTeamProjectConfigurations = default(IList<ITfsTeamProjectConfiguration>);
            //Insert your code here

            AvailableTfsTeamProjectConfigurations_RecalculateDependencies();
        }

        #endregion


        #region AvailableTimeFrames

        /// <summary>
        /// Field storage for property AvailableTimeFrames
        /// </summary>
        private IEnumerable<ITimeFrame> _AvailableTimeFrames = default(IEnumerable<ITimeFrame>);

        /// <summary>
        /// Property AvailableTimeFrames. TODODOC: Insert appropriate summary here
        /// </summary>
        public IEnumerable<ITimeFrame> AvailableTimeFrames
        {
            get { return _AvailableTimeFrames; }
            set { if (_AvailableTimeFrames == value) return; _AvailableTimeFrames = value; AvailableTimeFrames_RecalculateDependencies(); }
        }

        private void AvailableTimeFrames_RecalculateDependencies()
        {
            //Recalculate all properties here

            //NotifyOfPropertyChange is usually placed as last statement - first change all dependencies and then fire property change
            NotifyOfPropertyChange(() => AvailableTimeFrames);
        }

        //private void AvailableTimeFrames_Recalculate()
        //{
        //    _AvailableTimeFrames = default(IList<ITimeFrame>);
        //    //Insert your code here

        //    AvailableTimeFrames_RecalculateDependencies();
        //}

        #endregion


        #region CurrentRegisteredStatistics

        /// <summary>
        /// Field storage for property CurrentRegisteredStatistics
        /// </summary>
        private IEnumerable<IStatistic> _CurrentRegisteredStatistics = default(IEnumerable<IStatistic>);

        /// <summary>
        /// Property CurrentRegisteredStatistics. TODODOC: Insert appropriate summary here
        /// </summary>
        public IEnumerable<IStatistic> CurrentRegisteredStatistics
        {
            get { return _CurrentRegisteredStatistics; }
            set { if (_CurrentRegisteredStatistics == value) return; _CurrentRegisteredStatistics = value; CurrentRegisteredStatistics_RecalculateDependencies(); }
        }

        private void CurrentRegisteredStatistics_RecalculateDependencies()
        {
            //Recalculate all properties here

            //NotifyOfPropertyChange is usually placed as last statement - first change all dependencies and then fire property change
            SynchronizeStatistics();
            NotifyOfPropertyChange(() => CurrentRegisteredStatistics);
        }

        //private void CurrentRegisteredStatistics_Recalculate()
        //{
        //    if (ActiveStatisticViewModel == null)
        //    {
        //        if (AvailableStatistics != null)
        //        {
        //            _CurrentRegisteredStatistics = AvailableStatistics.Values;
        //        }
        //        else
        //        {
        //            _CurrentRegisteredStatistics = new List<IStatistic>();
        //        }
        //    }
        //    else
        //    {
        //        _ActiveStatisticViewModel.
        //    }


        //    //Insert your code here

        //    AvailableStatistics.Values.Where;
        //    CurrentRegisteredStatistics_RecalculateDependencies();
        //}

        #endregion


        #region AvailableStatistics

        /// <summary>
        /// Field storage for property AvailableStatistics
        /// </summary>
        private IReadOnlyDictionary<Guid, IStatistic> _AvailableStatistics = null;

        /// <summary>
        /// Property AvailableStatistics. TODODOC: Insert appropriate summary here
        /// </summary>
        public IReadOnlyDictionary<Guid, IStatistic> AvailableStatistics
        {
            get
            {
                return _AvailableStatistics;
            }
            protected set { if (_AvailableStatistics == value) return; _AvailableStatistics = value; AvailableStatistics_RecalculateDependencies(); }
        }

        private void AvailableStatistics_RecalculateDependencies()
        {
            //Recalculate all properties here

            //NotifyOfPropertyChange is usually placed as last statement - first change all dependencies and then fire property change
            NotifyOfPropertyChange(() => AvailableStatistics);
        }

        //private void AvailableStatistics_Recalculate()
        //{

        //    //Insert your code here

        //    AvailableStatistics_RecalculateDependencies();
        //}

        #endregion



        #region ActiveTfsTeamProjectConfiguration

        /// <summary>
        /// Field storage for property ActiveTfsTeamProjectConfiguration
        /// </summary>
        [DataMember]
        private ITfsTeamProjectConfiguration _ActiveTfsTeamProjectConfiguration = default(ITfsTeamProjectConfiguration);

        /// <summary>
        /// Property ActiveTfsTeamProjectConfiguration. TODODOC: Insert appropriate summary here
        /// </summary>

        public ITfsTeamProjectConfiguration ActiveTfsTeamProjectConfiguration
        {
            get { return _ActiveTfsTeamProjectConfiguration; }
            set { if (_ActiveTfsTeamProjectConfiguration == value) return; _ActiveTfsTeamProjectConfiguration = value; ActiveTfsTeamProjectConfiguration_RecalculateDependencies(); }
        }

        private void ActiveTfsTeamProjectConfiguration_RecalculateDependencies()
        {
            //Recalculate all properties here

            //NotifyOfPropertyChange is usually placed as last statement - first change all dependencies and then fire property change
            NotifyOfPropertyChange(() => ActiveTfsTeamProjectConfiguration);
        }

        private void ActiveTfsTeamProjectConfiguration_Recalculate()
        {
            _ActiveTfsTeamProjectConfiguration = default(ITfsTeamProjectConfiguration);
            //Insert your code here

            ActiveTfsTeamProjectConfiguration_RecalculateDependencies();
        }

        #endregion


        #region ActiveTimeFrame

        /// <summary>
        /// Field storage for property ActiveTimeFrame
        /// </summary>
        [DataMember]
        private ITimeFrame _ActiveTimeFrame = default(ITimeFrame);

        /// <summary>
        /// Property ActiveTimeFrame. TODODOC: Insert appropriate summary here
        /// </summary>
        public ITimeFrame ActiveTimeFrame
        {
            get { return _ActiveTimeFrame; }
            set { if (_ActiveTimeFrame == value) return; _ActiveTimeFrame = value; ActiveTimeFrame_RecalculateDependencies(); }
        }

        private void ActiveTimeFrame_RecalculateDependencies()
        {
            //Recalculate all properties here

            //NotifyOfPropertyChange is usually placed as last statement - first change all dependencies and then fire property change
            NotifyOfPropertyChange(() => ActiveTimeFrame);
        }

        //private void ActiveTimeFrame_Recalculate()
        //{
        //    _ActiveTimeFrame = default(ITimeFrame);
        //    //Insert your code here

        //    ActiveTimeFrame_RecalculateDependencies();
        //}

        #endregion


        #region ActiveStatistic

        /// <summary>
        /// Field storage for property ActiveStatistic
        /// </summary>
        private IStatistic _ActiveStatistic = default(IStatistic);

        /// <summary>
        /// Property ActiveStatistic. TODODOC: Insert appropriate summary here
        /// </summary>
        public IStatistic ActiveStatistic
        {
            get { return _ActiveStatistic; }
            set { if (_ActiveStatistic == value) return; _ActiveStatistic = value; ActiveStatistic_RecalculateDependencies(); }
        }

        private void ActiveStatistic_RecalculateDependencies()
        {
            //Recalculate all properties here

            //NotifyOfPropertyChange is usually placed as last statement - first change all dependencies and then fire property change
            NotifyOfPropertyChange(() => ActiveStatistic);
        }

        private void ActiveStatistic_Recalculate()
        {
            _ActiveStatistic = default(IStatistic);
            //Insert your code here

            ActiveStatistic_RecalculateDependencies();
        }

        #endregion


        #region ShowNormalized

        /// <summary>
        /// Field storage for property ShowNormalized
        /// </summary>
        private bool _ShowNormalized = default(bool);

        /// <summary>
        /// Property ShowNormalized. TODODOC: Insert appropriate summary here
        /// </summary>
        public bool ShowNormalized
        {
            get { return _ShowNormalized; }
            set { if (_ShowNormalized == value) return; _ShowNormalized = value; ShowNormalized_RecalculateDependencies(); }
        }

        private void ShowNormalized_RecalculateDependencies()
        {
            //Recalculate all properties here

            //NotifyOfPropertyChange is usually placed as last statement - first change all dependencies and then fire property change
            NotifyOfPropertyChange(() => ShowNormalized);
        }

        private void ShowNormalized_Recalculate()
        {
            _ShowNormalized = default(bool);
            //Insert your code here

            ShowNormalized_RecalculateDependencies();
        }

        #endregion


        #region Label

        /// <summary>
        /// Field storage for property Label
        /// </summary>
        [DataMember]
        private string _Label = default(string);

        /// <summary>
        /// Property Label. TODODOC: Insert appropriate summary here
        /// </summary>

        public string Label
        {
            get { return _Label; }
            set { if (_Label == value) return; _Label = value; Label_RecalculateDependencies(); }
        }

        private void Label_RecalculateDependencies()
        {
            //Recalculate all properties here

            //NotifyOfPropertyChange is usually placed as last statement - first change all dependencies and then fire property change
            NotifyOfPropertyChange(() => Label);
        }

        private void Label_Recalculate()
        {
            _Label = default(string);
            //Insert your code here

            Label_RecalculateDependencies();
        }

        #endregion

        #region Name

        /// <summary>
        /// Field storage for property Name
        /// </summary>
        [DataMember]
        private string _Name = default(string);

        /// <summary>
        /// Property Name. TODODOC: Insert appropriate summary here
        /// </summary>

        public string Name
        {
            get { return _Name; }
            set { if (_Name == value) return; _Name = value; Name_RecalculateDependencies(); }
        }

        private void Name_RecalculateDependencies()
        {
            //Recalculate all properties here

            //NotifyOfPropertyChange is usually placed as last statement - first change all dependencies and then fire property change
            NotifyOfPropertyChange(() => Name);
        }

        private void Name_Recalculate()
        {
            _Name = default(string);
            //Insert your code here

            Name_RecalculateDependencies();
        }

        #endregion



        [IgnoreDataMember]
        List<ReportingContextCachedItem> OldSelectedConfigurationItem;


        /// <summary>
        /// From the current TFSTeamProjectCollection gets all available TfsTeamProjectConfiguration objects and TimeFrame object 
        /// and synchronize the selected items in SelectableConfigurations collection
        /// </summary>
        /// <param name="selector"></param>
        public void SynchronizeTeamsAndTimeFrames()
        {

            var TeamConfigurationIsSelected = Selector.ITfsTeamProjectConfiguration_IsSelected;
            var TimeFrameIsSelected = Selector.ITimeFrame_IsSelected;

            if (TeamConfigurationIsSelected == null) TeamConfigurationIsSelected = (a) => true;
            if (TimeFrameIsSelected == null) TimeFrameIsSelected = (a) => true;



            //First we are adding TeamProjectConfigurations
            foreach (var tp in TfsTeamProjectCollectionConfiguration.Where(a => a.IsValidForStatistic))
            {
                var scTP = SelectableTfsTeamProjectConfigurations.FirstOrDefault(a => a.ItemToConfigure == tp);
                if (scTP == null) SelectableTfsTeamProjectConfigurations.Add(tp, TeamConfigurationIsSelected(tp));
            }

            AvailableTfsTeamProjectConfigurations = TfsTeamProjectCollectionConfiguration.Where(a => a.IsValidForStatistic);

            //Then ProjectCollectionTimeFrames
            foreach (var tf in TfsTeamProjectCollectionConfiguration.ProjectCollectionTimeFrames)
            {
                var scTF = SelectableTimeFrames.FirstOrDefault(a => a.ItemToConfigure == tf);
                if (scTF == null)
                {
                    SelectableTimeFrames.Add(tf, TimeFrameIsSelected(tf));
                }
            }

            AvailableTimeFrames = TfsTeamProjectCollectionConfiguration.ProjectCollectionTimeFrames;

            //Removing no more valid TeamProjectCollections or ProjectCollectionTimeFrames

            //First teams
            for (int i = 0; i < SelectableTfsTeamProjectConfigurations.Count; )
            {
                var tp = AvailableTfsTeamProjectConfigurations.FirstOrDefault(a => a == SelectableTfsTeamProjectConfigurations[i].ItemToConfigure);
                if (tp == null)
                {
                    SelectableTfsTeamProjectConfigurations.RemoveAt(i);
                }
                else
                {
                    i++;
                }
            }

            // Now we go through the collection and remove additional SelectableTimeFrames
            for (int i = 0; i < SelectableTimeFrames.Count; )
            {
                var tf = AvailableTimeFrames.FirstOrDefault(a => a == SelectableTimeFrames[i].ItemToConfigure);
                if (tf == null)
                {
                    SelectableTimeFrames.RemoveAt(i);
                }
                else
                {
                    i++;
                }
            }
        }

        public void SynchronizeStatistics()
        {
            //Statistic selector either comes from ReportingContextStrategy or default one will be used. The default strategy is select each strategy
            var statisticSelector = Selector.IStatistic_IsSelected ?? ((a) => true);
            //The statistics
            foreach (var stat in AvailableStatistics)
            {
                var scSTAT = SelectableStatistics.FirstOrDefault(a => a.ItemToConfigure == stat.Value);
                if (scSTAT == null)
                {
                    //first we get default value to be used from selected - this will decide if statistic should be selected
                    var statIsSelected = statisticSelector(stat.Value);
                    //then we try to find value in cache and use that value
                    var cachedItem = OldSelectedConfigurationItem.FirstOrDefault(a => a == stat.Value);
                    if (cachedItem != null) statIsSelected = cachedItem.IsSelected;
                    //Finally we are adding it to SelectableStatistics collection
                    SelectableStatistics.Add(stat.Value, statIsSelected);
                }
            }

            // Now we go through the collection and remove additional SelectableStatistics
            for (int i = 0; i < SelectableStatistics.Count; )
            {
                var stat = AvailableStatistics.FirstOrDefault(a => a.Value == SelectableStatistics[i].ItemToConfigure);
                bool isStatisticRegistered = true;
                if (CurrentRegisteredStatistics != null) isStatisticRegistered = CurrentRegisteredStatistics.Any(a => a == SelectableStatistics[i].ItemToConfigure);
                //If statistic is missing or not currently registered we put it to cache and remove it
                if (stat.Value == null || !isStatisticRegistered)
                {
                    var cselstat = SelectableStatistics[i];

                    //Implmentation of simple cache saving last setting for statistic, so if we change control which does not 
                    //support the statistic its setting will not be lost
                    var oStat = OldSelectedConfigurationItem.FirstOrDefault(a => a == cselstat.ItemToConfigure);
                    if (oStat != null)
                    {
                        //Already in the cache just update the value
                        oStat.IsSelected = cselstat.IsSelected;
                    }
                    else
                    {
                        //New in the cache - add the value
                        OldSelectedConfigurationItem.Add(new ReportingContextCachedItem() { CachedItem = cselstat.ItemToConfigure, IsSelected = cselstat.IsSelected });
                    }
                    SelectableStatistics.RemoveAt(i);
                }
                else
                {
                    i++;
                }
            }
        }


        public SelectedCollection<ITfsTeamProjectConfiguration> SelectedITfsTeamProjectConfigurations
        {
            get { return SelectableTfsTeamProjectConfigurations == null ? null : SelectableTfsTeamProjectConfigurations.SelectedItemsCollection; }
        }


        public SelectedCollection<ITimeFrame> SelectedITimeFrames
        {
            get { return SelectableTimeFrames == null ? null : SelectableTimeFrames.SelectedItemsCollection; }

        }

        public SelectedCollection<IStatistic> SelectedIStatistics
        {
            get { return SelectableStatistics == null ? null : SelectableStatistics.SelectedItemsCollection; }
        }

        public override string ToString()
        {
            return base.ToString();
        }

        public string ToString(string p)
        {
            if (p == "fi")
            {
                return string.Format("{0} : {1}", Name, ToString("i"));
            }
            else
                if (p == "i")
                {
                    return string.Format("ST:");
                }
                else
                    return base.ToString();
        }

        IReportingManager IReportingManager;
        IStatisticManager IStatisticManager;
        IPowersourceExt IPowersourceExt
        {
            get { return PowersourceConnector.GetExtensibilityPoint(); }
        }

        protected override bool OnInitializing()
        {

            IReportingManager = IPowersourceExt.ReportingManager;
            IStatisticManager = IPowersourceExt.StatisticManager;
            AvailableStatistics = IStatisticManager.AvailableStatistics;
            if (SelectableTfsTeamProjectConfigurations == null) SelectableTfsTeamProjectConfigurations = new SelectableConfiguration<ITfsTeamProjectConfiguration>();
            if (SelectableTimeFrames == null) SelectableTimeFrames = new SelectableConfiguration<ITimeFrame>();
            if (SelectableStatistics == null) SelectableStatistics = new SelectableConfiguration<IStatistic>();
            OldSelectedConfigurationItem = new List<ReportingContextCachedItem>();

            SynchronizeTeamsAndTimeFrames();
            SynchronizeStatistics();

            return AvailableStatistics != null;

        }

        protected override void OnDisposing()
        {
            AvailableStatistics = null;
        }




        public static IEnumerable<ReportingContext> CreateDefaults(ITfsTeamProjectCollectionConfiguration tfscc)
        {
            var ipext = PowersourceConnector.GetExtensibilityPoint();

            var rcDefault = new ReportingContext(tfscc, ipext.ReportingManager.ReportingContextStrategies.FirstOrDefault(a => a.GetType() == typeof(Default_ReportingContextStrategy)));
            rcDefault.Label = "Default";
            rcDefault.Name = "Default";
            rcDefault.Initialize();

            var rcAllSprints = new ReportingContext(tfscc, ipext.ReportingManager.ReportingContextStrategies.FirstOrDefault(a => a.GetType() == typeof(AllSelected_ReportingContextStrategy)));
            rcAllSprints.Label = "All sprints";
            rcAllSprints.Name = "All sprints";
            rcAllSprints.Initialize();

            var rcWorkItems = new ReportingContext(tfscc, ipext.ReportingManager.ReportingContextStrategies.FirstOrDefault(a => a.GetType() == typeof(WorkItemSummary_ReportingContextStrategy)));
            rcWorkItems.Label = "Work Items Summary";
            rcWorkItems.Name = "Work Items Summary";
            rcWorkItems.Initialize();

            var rcPBI = new ReportingContext(tfscc, ipext.ReportingManager.ReportingContextStrategies.FirstOrDefault(a => a.GetType() == typeof(PBISummary_ReportingContextStrategy)));
            rcPBI.Label = "PBI Summary";
            rcPBI.Name = "PBI Summary";
            rcPBI.Initialize();

            var rcTasks = new ReportingContext(tfscc, ipext.ReportingManager.ReportingContextStrategies.FirstOrDefault(a => a.GetType() == typeof(TaskSummary_ReportingContextStrategy)));
            rcTasks.Label = "Task Summary";
            rcTasks.Name = "Task Summary";
            rcTasks.Initialize();

            var rcInvalid = new ReportingContext(tfscc, ipext.ReportingManager.ReportingContextStrategies.FirstOrDefault(a => a.GetType() == typeof(InvalidWorkSummary_ReportingContextStrategy)));
            rcInvalid.Label = "Invalid Work Summary";
            rcInvalid.Name = "Invalid Work Summary";
            rcInvalid.Initialize();

            var rcPlanning = new ReportingContext(tfscc, ipext.ReportingManager.ReportingContextStrategies.FirstOrDefault(a => a.GetType() == typeof(PlanningSummary_ReportingContextStrategy)));
            rcPlanning.Label = "Planning Summary";
            rcPlanning.Name = "Planning Summary";
            rcPlanning.Initialize();

            var rcCode = new ReportingContext(tfscc, ipext.ReportingManager.ReportingContextStrategies.FirstOrDefault(a => a.GetType() == typeof(CodeSummary_ReportingContextStrategy)));
            rcCode.Label = "Source Code Summary";
            rcCode.Name = "Source Code Summary";
            rcCode.Initialize();

            return new[] { rcDefault, rcAllSprints, rcWorkItems, rcPBI, rcTasks, rcInvalid, rcPlanning, rcCode };
        }
    }
}
